package com.hmall.search.controller;

import com.alibaba.fastjson.JSON;
import com.hmall.common.dto.PageDTO;
import com.hmall.search.pojo.ItemDoc;
import com.hmall.search.pojo.RequestParams;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.lucene.search.function.CombineFunction;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.elasticsearch.search.suggest.SuggestBuilder;
import org.elasticsearch.search.suggest.SuggestBuilders;
import org.elasticsearch.search.suggest.completion.CompletionSuggestion;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;

import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

@RestController
@Slf4j
@RequestMapping("/search")
public class SearchController {

    @Autowired
    private RestHighLevelClient client;

    /**
     * 自动补全
     *
     * @param key
     * @return
     */
    @GetMapping("/suggestion")
    public List<String> suggestion(@RequestParam("key") String key) {
        SearchRequest request = new SearchRequest("item");
        request.source().suggest(new SuggestBuilder()
                .addSuggestion("completion_analyzer", SuggestBuilders.completionSuggestion("suggestion").skipDuplicates(true).size(10).prefix(key)));

        try {
            CompletionSuggestion suggestion = client.search(request, RequestOptions.DEFAULT).getSuggest().getSuggestion("completion_analyzer");
            return suggestion.getOptions().stream().map(o -> o.getText().string()).collect(Collectors.toList());
        } catch (IOException e) {
            return Collections.emptyList();
        }
    }

    @PostMapping("/list")
    public PageDTO<ItemDoc> list(@RequestBody RequestParams params) throws IOException {
        //构建请求
        SearchRequest request = searchRequest(params);
        //发送请求
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        //解析响应
        return handleResponse(response);
    }

    private PageDTO<ItemDoc> handleResponse(SearchResponse response) {
        //解析响应结果
        PageDTO<ItemDoc> pageDTO = new PageDTO<>();
        SearchHits searchHits = response.getHits();
        //获取相应条数
        long total = searchHits.getTotalHits().value;
        pageDTO.setTotal(total);
        //文档数组
        SearchHit[] hits = searchHits.getHits();
        List<ItemDoc> itemDocs = new ArrayList<>();

        for (SearchHit hit : hits) {
            //获取文档source
            String source = hit.getSourceAsString();
            //反序列化
            ItemDoc itemDoc = JSON.parseObject(source, ItemDoc.class);
            //获取高亮字段
            Map<String, HighlightField> highlightFields = hit.getHighlightFields();
            if (!CollectionUtils.isEmpty(highlightFields)) {
                //高亮数组对象
                HighlightField highlightField = highlightFields.get("name");
                //判断获取高亮内容
                if (highlightField != null) {
                    String name = highlightField.getFragments()[0].string();
                    itemDoc.setName(name);
                }
            }
            //放入集合
            itemDocs.add(itemDoc);
        }
        pageDTO.setList(itemDocs);
        return pageDTO;
    }

    private SearchRequest searchRequest(RequestParams params) {
        //构建请求对象
        SearchRequest request = new SearchRequest("item");
        //准备数据
        String key = params.getKey();//关键字
        Integer page = params.getPage();//分页数
        Integer size = params.getSize();//每页展示书
        String brand = params.getBrand();//品牌
        String sortBy = params.getSortBy();//
        String category = params.getCategory();//分类
        Integer minPrice = params.getMinPrice();//最小价格
        Integer maxPrice = params.getMaxPrice();//最高价格

        //复合查询,讲条件放入query中
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();

        //判断key是否输入,根据一all进行查询
        if (!StringUtils.isEmpty(key)) {
            boolQuery.must(QueryBuilders.matchQuery("all", key));
        } else {
            //没有输入matchall
            boolQuery.must(QueryBuilders.matchAllQuery());
        }
        //调教过滤
        if (!StringUtils.isEmpty(brand)) {
            boolQuery.should(QueryBuilders.termQuery("brand", brand));
        }
        if (!StringUtils.isEmpty(category)) {
            boolQuery.must(QueryBuilders.termQuery("category", category));
        }
        if (minPrice != null) {
            boolQuery.filter(QueryBuilders.rangeQuery("price").gte(minPrice * 100));
        }
        if (maxPrice != null) {
            boolQuery.filter(QueryBuilders.rangeQuery("price").lte(maxPrice * 100));
        }
        //sold 销量降序,价格升序排序
        if (!StringUtils.isEmpty(sortBy)) {
            if (sortBy.equals("sold")) {
                request.source().sort("sold", SortOrder.DESC);
            }
            if (sortBy.equals("price")) {
                request.source().sort("price", SortOrder.ASC);
            }
        }
        //关键字高亮显示
        request.source().highlighter(
                new HighlightBuilder()
                        .field("name")
                        .requireFieldMatch(false)
        );
        //function
        FunctionScoreQueryBuilder functionScore = QueryBuilders.functionScoreQuery(boolQuery,
                new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{
                        new FunctionScoreQueryBuilder.FilterFunctionBuilder(
                                QueryBuilders.termQuery("isAD", true),
                                ScoreFunctionBuilders.weightFactorFunction(5)
                        )}
        ).boostMode(CombineFunction.MULTIPLY); // 权重策略
        request.source().query(
                functionScore
        );
        //分页
        request.source().from((page - 1) * size).size(size);
        return request;
    }
}
